<?php
/**
 * FTP操作类
 * 
 * @copyright 	Copyright (c) 2011 thinkapk.com All rights reserved.
 * @package 	ThinkAPK
 * @link 		http://www.thinkapk.com/
 * @author 		richie <richbossman@163.com>
 * @version 	$Id: Ftp.class.php 7413 2011-05-05 10:09:08Z richie $
 */
class cls_ftp {
	//常量
	const ASCII = FTP_ASCII;
	const TEXT = FTP_TEXT;
	const BINARY = FTP_BINARY;
	const IMAGE = FTP_IMAGE;
	const TIMEOUT_SEC = FTP_TIMEOUT_SEC;
	const AUTOSEEK = FTP_AUTOSEEK;
	const AUTORESUME = FTP_AUTORESUME;
	const FAILED = FTP_FAILED;
	const FINISHED = FTP_FINISHED;
	const MOREDATA = FTP_MOREDATA;
	
	//别名设置
	private static $aliases = array(
		'sslconnect' => 'ssl_connect',
		'getoption' => 'get_option',
		'setoption' => 'set_option',
		'nbcontinue' => 'nb_continue',
		'nbfget' => 'nb_fget',
		'nbfput' => 'nb_fput',
		'nbget' => 'nb_get',
		'nbput' => 'nb_put',
	);
	
	private $resource;
	private $state;
	private $errorMsg = NULL;
	public $domain;

	//构造函数
	public function __construct($parts = array())
	{
		if (!extension_loaded('ftp')) {
			$this->errorMsg = ("PHP的FTP扩展未加载。");
			return;
		}
		if ($parts && is_array($parts)) {
			$this->connect($parts['host'], empty($parts['port']) ? NULL : (int) $parts['port'], empty($parts['timeout']) ? 5 : intval($parts['timeout']));
			$this->login($parts['user'], $parts['pass']);
			$this->pasv(TRUE);
			if (isset($parts['path'])) {
				$this->chdir($parts['path']);
			}
		}
	}
	
	public function __destruct(){
		$this->close();
	}



	/**
	 * Magic method (do not call directly).
	 * @param  string  method name
	 * @param  array   arguments
	 * @return mixed
	 * @throws Exception
	 * @throws FtpException
	 */
	public function __call($name, $args)
	{
		$name = strtolower($name);
		$silent = strncmp($name, 'try', 3) === 0;
		$func = $silent ? substr($name, 3) : $name;
		$func = 'ftp_' . (isset(self::$aliases[$func]) ? self::$aliases[$func] : $func);

		if (!function_exists($func)) {
			$this->errorMsg = ("不能访问 这个方法： cls_ftp::$name().");
			return false;
		}

		$this->errorMsg = NULL;
		set_error_handler(array($this, '_errorHandler'));

		if ($func === 'ftp_connect' || $func === 'ftp_ssl_connect') {
			$this->state = array($name => $args);
			$this->resource = call_user_func_array($func, $args);
			$res = NULL;

		} elseif (!is_resource($this->resource)) {
			restore_error_handler();
			$this->errorMsg = ("连接FTP服务器失败。");
			return false;

		} else {
			if ($func === 'ftp_login' || $func === 'ftp_pasv') {
				$this->state[$name] = $args;
			}

			array_unshift($args, $this->resource);
			$res = call_user_func_array($func, $args);

			if ($func === 'ftp_chdir' || $func === 'ftp_cdup') {
				$this->state['chdir'] = array(ftp_pwd($this->resource));
			}
		}

		restore_error_handler();
		if (!$silent && $this->errorMsg !== NULL) {
			if (ini_get('html_errors')) {
				$this->errorMsg = html_entity_decode(strip_tags($this->errorMsg));
			}

			if (($a = strpos($this->errorMsg, ': ')) !== FALSE) {
				$this->errorMsg = substr($this->errorMsg, $a + 2);
			}
			return false;
			//throw new FtpException($this->errorMsg);
		}

		return $res;
	}



	/**
	 * Internal error handler. Do not call directly.
	 */
	public function _errorHandler($code, $message)
	{
		$this->errorMsg = $message;
	}
	
	/**
	 * error
	 *
	 * @return string
	 */
	public function getError(){
		return $this->errorMsg;
	}



	/**
	 * Reconnects to FTP server.
	 * @return void
	 */
	public function reconnect()
	{
		@ftp_close($this->resource); // intentionally @
		foreach ($this->state as $name => $args) {
			call_user_func_array(array($this, $name), $args);
		}
	}



	/**
	 * Checks if file or directory exists.
	 * @param  string
	 * @return bool
	 */
	public function fileExists($file)
	{
		return is_array($this->nlist($file));
	}



	/**
	 * Checks if directory exists.
	 * @param  string
	 * @return bool
	 */
	public function isDir($dir)
	{
		$current = $this->pwd();
		try {
			$this->chdir($dir);
		} catch (FtpException $e) {
		}
		$this->chdir($current);
		return empty($e);
	}



	/**
	 * Recursive creates directories.
	 * @param  string
	 * @return void
	 */
	public function mkDirRecursive($dir)
	{
		$parts = explode('/', $dir);
		$path = '';
		while (!empty($parts)) {
			$path .= array_shift($parts);
			try {
				if ($path !== '') $this->mkdir($path);
			} catch (FtpException $e) {
				if (!$this->isDir($path)) {
					throw new FtpException("不能创建目录： '$path'.");
				}
			}
			$path .= '/';
		}
	}


	/**
	 * Recursive deletes path.
	 * @param  string
	 * @return void
	 */
	public function deleteRecursive($path)
	{
		if (!$this->tryDelete($path)) {
			foreach ((array) $this->nlist($path) as $file) {
				if ($file !== '.' && $file !== '..') {
					$this->deleteRecursive(strpos($file, '/') === FALSE ? "$path/$file" : $file);
				}
			}
			$this->rmdir($path);
		}
	}

}

class FtpException extends Exception
{
}

class ftp_apk {
	//错误信息
	private $errmsg = '';
	//FTP信息
	private $ftpinfo;
	//FTP对象
	private $ftp;
	//FILES信息
	private $FILES;
	//ID
	public $appId=0;
	
	/**
	 * 适用于 APK的FTP上传
	 *
	 * @param string $file
	 * @param string $dirname
	 * @return string
	 */
	function uploadAPK($file='', $dirname='apks'){
		if($this->connectFtp() == false){
			return false;
		}
		$this->FILES['name'] = $file;
		$dir = $this->getNewDir($dirname);
		$this->ftp->mkDirRecursive($dir);
		$filename = $dir.$this->getNewFileName();
		if($this->ftp->put($filename, ROOT_PATH."sapload/".$file, FTP_BINARY)){
			@unlink(ROOT_PATH.'sapload/'.$file);
			return array('url' => $this->ftpinfo['domain'].$filename);
		}else{
			$this->errmsg = $this->ftp->getError();
			return false;
		}
	}
	
	/**
	 * 随机调取一个FTP信息
	 *
	 * @return array
	 */
	function randFtpServer(){
		$ftpServer = common_config('apk_ftps');
		$total = count($ftpServer);
		if($total == 0) return false;
		$key = rand(0, $total-1);
		if(!isset($ftpServer[$key])) return false;
		$this->ftpinfo = $ftpServer[$key];
		return true;
	}
	
	/**
	 * 连接FTP
	 *
	 * @return object
	 */
	function connectFtp(){
		if($this->randFtpServer() == false){
			$this->errmsg = 'FTP信息读取失败';
			return false;
		}
		$ftp = new cls_ftp($this->ftpinfo);
		if($ftp->getError()){
			$this->errmsg = $ftp->getError();
			return false;
		}
		$this->ftp = $ftp;
		return true;
	}
	
	/**
	 * 取得文件后缀
	 *
	 * @return array
	 */
	function getExt(){
		$pathinfo = pathinfo($this->FILES['name']);
		return isset($pathinfo['extension']) ? $pathinfo['extension'] : '';
	}
	
	/**
	 * 取得新上传文件名
	 *
	 * @return string
	 */
	function getNewFileName(){
		return 'thinkapk.com.'.(!empty($this->appId)?$this->appId:time()).'.'.$this->getExt();
	}
	
	/**
	 * 取得新文件存放的目录
	 *
	 * @param string $dirname
	 * @return string
	 */
	function getNewDir($dirname='uploads'){
		if(!empty($this->appId)){
			return $dirname.'/'.date('Ym', time()).'/'.$this->appId.'/';
		}
		$md5 = md5($this->FILES['name']);
		return $dirname.'/'.$md5{0}.'/'.date('Ym', time()).'/'.date('d', time()).'/';
	}
	
	/**
	 * 取得错误信息
	 *
	 * @return string
	 */
	function getError(){
		return $this->errmsg;
	}
}

class ftp_upload {
	//错误信息
	private $errmsg = '';
	//FTP信息
	private $ftpinfo;
	//FTP对象
	private $ftp;
	//FILES信息
	private $FILES;
	//ID
	public $appId=0;
	
	/**
	 * 单个文件上传
	 *
	 */
	function upLoadFileOne($file=array(), $dirname='uploads'){
		$this->FILES = $file;
		if(!in_array($this->FILES['type'], array('image/gif', 'image/pjpeg', 'image/jpeg', 'image/x-png', 'image/png'))){
			$this->errmsg = '上传的文件类型不正确';
			return false;
		}
		
		if($this->connectFtp() == false){
			return false;
		}
		$dir = $this->getNewDir($dirname);
		
		$this->ftp->mkDirRecursive($dir);
		$filename = $dir.$this->getNewFileName();
		if($this->ftp->put($filename, $this->FILES['tmp_name'], FTP_BINARY)){
			return array('url' => $this->ftpinfo['domain'].$filename);
		}else{
			$this->errmsg = $this->ftp->getError();
			return false;
		}
	}
	
	/**
	 * 适用于FCKeditor的FTP上传
	 *
	 * @param string $file
	 * @param string $dirname
	 * @return string
	 */
	function uploadForFCKeditor($file='', $dirname='uploads'){
		if($this->connectFtp() == false){
			return false;
		}
		$this->FILES['name'] = $file;
		$dir = $this->getNewDir($dirname);
		$this->ftp->mkDirRecursive($dir);
		$filename = $dir.basename($file);
		if($this->ftp->put($filename, ROOT_PATH."../".$file, FTP_BINARY)){
			return array('url' => $this->ftpinfo['domain'].$filename);
		}else{
			$this->errmsg = $this->ftp->getError();
			return false;
		}
	}
	
	/**
	 * 随机调取一个FTP信息
	 *
	 * @return array
	 */
	function randFtpServer(){
		$ftpServer = common_config('ftps');
		$total = count($ftpServer);
		if($total == 0) return false;
		$key = rand(0, $total-1);
		if(!isset($ftpServer[$key])) return false;
		$this->ftpinfo = $ftpServer[$key];
		return true;
	}
	
	/**
	 * 连接FTP
	 *
	 * @return object
	 */
	function connectFtp(){
		if($this->randFtpServer() == false){
			$this->errmsg = 'FTP信息读取失败';
			return false;
		}
		$ftp = new cls_ftp($this->ftpinfo);
		if($ftp->getError()){
			$this->errmsg = $ftp->getError();
			return false;
		}
		$this->ftp = $ftp;
		return true;
	}
	
	/**
	 * 取得文件后缀
	 *
	 * @return array
	 */
	function getExt(){
		$pathinfo = pathinfo($this->FILES['name']);
		return isset($pathinfo['extension']) ? $pathinfo['extension'] : '';
	}
	
	/**
	 * 取得新上传文件名
	 *
	 * @return string
	 */
	function getNewFileName(){
		$size = getimagesize($this->FILES['tmp_name']);
		return time().rand(0, 100).'-'.(isset($size[0])?$size[0]:'0').'x'.(isset($size[1])?$size[1]:'0').'.'.$this->getExt();
	}
	
	/**
	 * 取得新文件存放的目录
	 *
	 * @param string $dirname
	 * @return string
	 */
	function getNewDir($dirname='uploads'){
		if(!empty($this->appId)){
			return $dirname.'/'.date('Ym', time()).'/'.$this->appId.'/';
		}
		$md5 = md5($this->FILES['name']);
		return $dirname.'/'.$md5{0}.'/'.date('Ym', time()).'/'.date('d', time()).'/';
	}
	
	/**
	 * 取得错误信息
	 *
	 * @return string
	 */
	function getError(){
		return $this->errmsg;
	}
}
?>